#include "bool_property.h"

//------------------------------------------------------------------------------------------------------
BoolProperty::BoolProperty(bool defaultValue, QObject* parent)
    : QObject(parent), _value(defaultValue), _internalValue(defaultValue), _securityValue(JET_TRUE)
{
}

//------------------------------------------------------------------------------------------------------
BoolProperty::BoolProperty(const BoolProperty& prop, QObject* parent)
    : QObject(parent), _value(prop._value), _internalValue(prop._internalValue), _securityValue(JET_TRUE),
    _linkedProperties(prop._linkedProperties)
{
}

//------------------------------------------------------------------------------------------------------
bool BoolProperty::Value() const
{
    return _value;
}

//------------------------------------------------------------------------------------------------------
bool BoolProperty::InternalValue() const
{
    return _internalValue;
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::operator =(bool value)
{
    SetValue(value);
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::operator =(const BoolProperty& prop)
{
    SetValue(prop.Value());
}

//------------------------------------------------------------------------------------------------------
bool BoolProperty::operator ==(bool value) const
{
    return _value == value;
}

//------------------------------------------------------------------------------------------------------
bool BoolProperty::operator ==(const BoolProperty& prop) const
{
    return _value == prop._value;
}

//------------------------------------------------------------------------------------------------------
bool BoolProperty::operator !=(bool value) const
{
    return _value != value;
}

//------------------------------------------------------------------------------------------------------
bool BoolProperty::operator !=(const BoolProperty& prop) const
{
    return _value != prop._value;
}

//------------------------------------------------------------------------------------------------------
JET_BOOL BoolProperty::AddDependency(BoolProperty* prop, bool value)
{
    Q_ASSERT(prop != nullptr);
    //check prop exist
    std::vector<std::pair<BoolProperty*, bool>>::const_iterator itBase = _linkedProperties.begin();
    while (itBase != _linkedProperties.end())
    {
        if (prop == itBase->first)
            return JET_TRUE;
        ++itBase;
    }
    if (CyclicCheck(this, prop))
    {
        Q_ASSERT(JET_FALSE);
        return JET_FALSE;
    }

    connect(prop, SIGNAL(ValueChanged(bool)), this, SLOT(OnLinkedPropertyValueChanged(bool)), Qt::DirectConnection);
    _linkedProperties.push_back(std::pair<BoolProperty*, bool>(prop, value));
    EvaluateValue();
    return JET_TRUE;
}

//------------------------------------------------------------------------------------------------------
JET_BOOL BoolProperty::RemoveDependency(BoolProperty* prop)
{
    std::vector<std::pair<BoolProperty*, bool>>::iterator it = _linkedProperties.begin();
    for (; it != _linkedProperties.end(); it++)
    {
        if (it->first == prop)
        {
            _linkedProperties.erase(it);
            EvaluateValue();
            return JET_TRUE;
        }
    }
    return JET_FALSE;
}

//------------------------------------------------------------------------------------------------------
BoolProperty::operator bool() const
{
    return Value();
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::SetValue(bool value)
{
    if (_internalValue == value) return;
    _internalValue = value;
    EvaluateValue();
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::SetSecurityValue(bool value)
{
    if (_securityValue == value)  return;
    _securityValue = value;
    EvaluateValue();
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::SetToTrue()
{
    SetValue(JET_TRUE);
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::SetToFalse()
{
    SetValue(JET_FALSE);
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::SetValue(JET_SINT value)
{
    if (value == 0)
    {
        SetValue(JET_FALSE);
    }
    else
    {
        SetValue(JET_TRUE);
    }
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::OnLinkedPropertyValueChanged(bool newValue)
{
    (void)newValue;
    EvaluateValue();
}

//------------------------------------------------------------------------------------------------------
void BoolProperty::EvaluateValue()
{
    // Evaluate final value, it is true only when it's internal value is set to true,
    // and all it's dependent properties' values equal to assigned values.
    bool evaluatedValue = _internalValue && _securityValue;
    if (evaluatedValue)
    {
        std::vector<std::pair<BoolProperty*, bool>>::const_iterator it = _linkedProperties.begin();
        while (it != _linkedProperties.end())
        {
            evaluatedValue = it->first->_value == it->second;
            if (!evaluatedValue) break;
            ++it;
        }
    }

    // Do nothing if value is not changed.
    if (_value == evaluatedValue) return;

    // Emit signals.
    _value = evaluatedValue;
    emit ValueChanged(_value);
    if (_value)
    {
        emit FalseSet();
    }
    else
    {
        emit TrueSet();
    }
}

//------------------------------------------------------------------------------------------------------
JET_BOOL BoolProperty::CyclicCheck(BoolProperty* base, BoolProperty* dependentProp)
{
    if (base == dependentProp) return JET_TRUE;
    // Recursively check all dependent properties in the being checked property.
    std::vector<std::pair<BoolProperty*, bool>>::const_iterator it = dependentProp->_linkedProperties.begin();
    while (it != dependentProp->_linkedProperties.end())
    {
        if (CyclicCheck(base, it->first))
            return JET_TRUE;
        ++it;
    }
    return JET_FALSE;
}